`timescale 1ns/1ps
`default_nettype none

/* NOTE:
* - 对输入像素数据进行处理
* - 总是处理8的倍数个像素
*/

module pixel_process (
    // system signal
    input  wire         I_sclk,
    input  wire         I_rst_n,
    // config
    input  wire         I_cfg_light_adj_en,  // 全局亮度调整
    input  wire         I_cfg_temp_adj_en,   // 全局色温调整
    input  wire         I_cfg_color_adj_en,  // 全局色度调整
    input  wire         I_cfg_pixel_adj_en,  // 逐点色度调整
    input  wire         I_cfg_gap_adj_en,    // 缝隙调整
    input  wire [15:0]  I_cfg_temp_coe_r,    // 色温调整系数
    input  wire [15:0]  I_cfg_temp_coe_g,    // 色温调整系数
    input  wire [15:0]  I_cfg_temp_coe_b,    // 色温调整系数
    input  wire [15:0]  I_cfg_light_coe,     // 整体亮度系数
    input  wire [15:0]  I_cfg_color_coe_r0,  // 全局色度调整系数
    input  wire [15:0]  I_cfg_color_coe_g0,  // 全局色度调整系数
    input  wire [15:0]  I_cfg_color_coe_b0,  // 全局色度调整系数
    input  wire [15:0]  I_cfg_color_coe_r1,  // 全局色度调整系数
    input  wire [15:0]  I_cfg_color_coe_g1,  // 全局色度调整系数
    input  wire [15:0]  I_cfg_color_coe_b1,  // 全局色度调整系数
    input  wire [15:0]  I_cfg_color_coe_r2,  // 全局色度调整系数
    input  wire [15:0]  I_cfg_color_coe_g2,  // 全局色度调整系数
    input  wire [15:0]  I_cfg_color_coe_b2,  // 全局色度调整系数
    input  wire         I_cfg_contrast_en,   // 对比度使能
    input  wire [7:0]   I_cfg_contrast_coe,  // 对比度系数
    // pixel coe
    output wire         O_pixel_coe_ack, // 当前系数使用完毕，切换到下一个系数
    input  wire [15:0]  I_pixel_coe_r,   // 逐点调整系数
    input  wire [15:0]  I_pixel_coe_g,   // 逐点调整系数
    input  wire [15:0]  I_pixel_coe_b,   // 逐点调整系数
    // gap coe
    output wire         O_gap_coe_ack,
    input  wire [15:0]  I_gap_coe_val,
    // gamma
    output wire         O_gamma_start,
    output wire [7:0]   O_gamma_data,
    input  wire [15:0]  I_gamma_result,
    // input pixel
    input  wire         I_pixel_start,
    input  wire [7:0]   I_pixel_data,
    // output result
    output wire         O_result_valid,
    output wire [31:0]  O_result_data
);
//------------------------Parameter----------------------
// latency
localparam
    CONTRAST_LATENCY  = 4,
    GAMMA_LATENCY     = 7,
    LIGHT_ADJ_LATENCY = 1;

// shift register length
localparam
    L0 = CONTRAST_LATENCY - 1,
    L1 = CONTRAST_LATENCY + GAMMA_LATENCY - 1,
    L2 = CONTRAST_LATENCY + GAMMA_LATENCY + LIGHT_ADJ_LATENCY * 2 - 1;

//------------------------Local signal-------------------
// input pixel
reg  [2:0]  pixel_index;
reg  [2:0]  pixel_color;
reg         pixel_valid;
reg  [L1:0] valid_sr;
reg  [L2:0] start_sr;

// contrast
wire [7:0]  contrast_data;

// gamma
reg  [L0:0] gamma_start;

// color temperature
wire        ta_data_en;
wire [15:0] ta_data_in;
reg  [2:0]  ta_data_color;
wire        ta_valid;
wire [15:0] ta_data_out;

// global light
wire        la_data_en;
wire [15:0] la_data_in;
wire [2:0]  la_data_color;
wire        la_valid;
wire [15:0] la_data_out;

//// global color adjust
//wire        gca_data_en;
//reg  [15:0] gca_r_in;
//reg  [15:0] gca_g_in;
//wire [15:0] gca_b_in;
//wire        gca_valid;
//wire [15:0] gca_r_out;
//wire [15:0] gca_g_out;
//wire [15:0] gca_b_out;
//reg  [2:0]  gca_color;

// gap adjust
wire        ga_data_en;
wire [15:0] ga_data_in;
wire        ga_valid;
wire [15:0] ga_r_out;
wire [15:0] ga_g_out;
wire [15:0] ga_b_out;

// pixel color adjust
wire        pca_data_en;
wire [15:0] pca_r_in;
wire [15:0] pca_g_in;
wire [15:0] pca_b_in;
wire        pca_valid;
wire [15:0] pca_r_out;
wire [15:0] pca_g_out;
wire [15:0] pca_b_out;

// output result
reg          odd_flag;
wire [47:0]  pixel_data;
reg  [47:0]  pixel_buf;
reg          result_valid;
reg  [1:0]   cnt2;
wire [95:0]  raw_pixel;
reg  [95:0]  result;

//------------------------Instantiation------------------
// contrast_adjust
contrast_adjust contrast_adj (/*{{{*/
    .I_sclk          ( I_sclk ),
    .I_rst_n         ( I_rst_n ),
    .I_contrast_en   ( I_cfg_contrast_en ),
    .I_contrast_coe  ( I_cfg_contrast_coe ),
    .I_data_in       ( I_pixel_data ),
    .O_data_out      ( contrast_data )
);/*}}}*/

// light_adjust(temperature)
light_adjust temp_adj (/*{{{*/
    .I_sclk       ( I_sclk ),
    .I_rst_n      ( I_rst_n ),
    .I_adjust_en  ( I_cfg_temp_adj_en),
    .I_data_en    ( ta_data_en ),
    .I_data_in    ( ta_data_in ),
    .I_data_color ( ta_data_color ),
    .I_coe_r      ( I_cfg_temp_coe_r ),
    .I_coe_g      ( I_cfg_temp_coe_g ),
    .I_coe_b      ( I_cfg_temp_coe_b ),
    .O_valid      ( ta_valid ),
    .O_data_out   ( ta_data_out )
);/*}}}*/

// light_adjust(global)
light_adjust light_adj (/*{{{*/
    .I_sclk       ( I_sclk ),
    .I_rst_n      ( I_rst_n ),
    .I_adjust_en  ( I_cfg_light_adj_en),
    .I_data_en    ( la_data_en ),
    .I_data_in    ( la_data_in ),
    .I_data_color ( la_data_color ),
    .I_coe_r      ( I_cfg_light_coe ),
    .I_coe_g      ( 16'd0 ),
    .I_coe_b      ( 16'd0 ),
    .O_valid      ( la_valid ),
    .O_data_out   ( la_data_out )
);/*}}}*/

//// global_color_adjust
//global_color_adjust global_color_adj (/*{{{*/
//    .I_sclk      ( I_sclk ),
//    .I_rst_n     ( I_rst_n ),
//    .I_adjust_en ( I_cfg_color_adj_en ),
//    .I_coe_r0    ( I_cfg_color_coe_r0 ),
//    .I_coe_g0    ( I_cfg_color_coe_g0 ),
//    .I_coe_b0    ( I_cfg_color_coe_b0 ),
//    .I_coe_r1    ( I_cfg_color_coe_r1 ),
//    .I_coe_g1    ( I_cfg_color_coe_g1 ),
//    .I_coe_b1    ( I_cfg_color_coe_b1 ),
//    .I_coe_r2    ( I_cfg_color_coe_r2 ),
//    .I_coe_g2    ( I_cfg_color_coe_g2 ),
//    .I_coe_b2    ( I_cfg_color_coe_b2 ),
//    .I_data_en   ( gca_data_en ),
//    .I_r_in      ( gca_r_in ),
//    .I_g_in      ( gca_g_in ),
//    .I_b_in      ( gca_b_in ),
//    .O_valid     ( gca_valid ),
//    .O_r_out     ( gca_r_out ),
//    .O_g_out     ( gca_g_out ),
//    .O_b_out     ( gca_b_out )
//);/*}}}*/

// gap_adjust
gap_adjust gap_adj (/*{{{*/
    .I_sclk      ( I_sclk ),
    .I_rst_n     ( I_rst_n ),
    .I_adjust_en ( I_cfg_gap_adj_en ),
    .O_coe_ack   ( O_gap_coe_ack ),
    .I_coe_val   ( I_gap_coe_val ),
    .I_data_en   ( ga_data_en ),
    .I_data_in   ( ga_data_in ),
    .O_valid     ( ga_valid ),
    .O_r_out     ( ga_r_out ),
    .O_g_out     ( ga_g_out ),
    .O_b_out     ( ga_b_out )
);/*}}}*/

// color_adjust
color_adjust pixel_color_adj (/*{{{*/
    .I_sclk      ( I_sclk ),
    .I_rst_n     ( I_rst_n ),
    .I_adjust_en ( I_cfg_pixel_adj_en ),
    .I_data_en   ( pca_data_en ),
    .I_r_in      ( pca_r_in ),
    .I_g_in      ( pca_g_in ),
    .I_b_in      ( pca_b_in ),
    .O_coe_ack   ( O_pixel_coe_ack ),
    .I_coe_r     ( I_pixel_coe_r ),
    .I_coe_g     ( I_pixel_coe_g ),
    .I_coe_b     ( I_pixel_coe_b ),
    .O_valid     ( pca_valid ),
    .O_r_out     ( pca_r_out ),
    .O_g_out     ( pca_g_out ),
    .O_b_out     ( pca_b_out )
);/*}}}*/

//------------------------Body---------------------------
//{{{+++++++++++++++++++++input pixel++++++++++++++++++++
// pixel_index
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        pixel_index <= 1'b0;
    else if (I_pixel_start)
        pixel_index <= 1'b0;
    else if (pixel_index != 3'd7 && pixel_color[2])
        pixel_index <= pixel_index + 1'b1;
end

// pixel_color
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        pixel_color <= 3'b001;
    else if (I_pixel_start)
        pixel_color <= 3'b001;
    else
        pixel_color <= {pixel_color[1:0], pixel_color[2]};
end

// pixel_valid
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        pixel_valid <= 1'b0;
    else if (I_pixel_start)
        pixel_valid <= 1'b1;
    else if (pixel_index == 3'd7 && pixel_color[2])
        pixel_valid <= 1'b0;
end

// valid_sr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        valid_sr <= 1'b0;
    else
        valid_sr <= {valid_sr[L1-1:0], pixel_valid};
end

// start_sr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        start_sr <= 1'b0;
    else
        start_sr <= {start_sr[L2-1:0], I_pixel_start};
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++gamma++++++++++++++++++++++++++
assign O_gamma_start = gamma_start[L0];
assign O_gamma_data  = contrast_data;

// gamma_start
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        gamma_start <= 1'b0;
    else if (pixel_valid && pixel_color[0])
        gamma_start <= {gamma_start[L0-1:0], 1'b1};
    else
        gamma_start <= {gamma_start[L0-1:0], 1'b0};
end

//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++color temperature++++++++++++++
assign ta_data_en    = valid_sr[L1];
assign ta_data_in    = I_gamma_result;

// ta_data_color
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        ta_data_color <= 3'b001;
    else if (start_sr[L1])
        ta_data_color <= 3'b001;
    else
        ta_data_color <= {ta_data_color[1:0], ta_data_color[2]};
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++global light+++++++++++++++++++
assign la_data_en    = ta_valid;
assign la_data_in    = ta_data_out;
assign la_data_color = 3'b001;
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

////{{{+++++++++++++++++++++global color adjust++++++++++++
//assign gca_data_en = la_valid & gca_color[2];
//assign gca_b_in    = la_data_out;
//
//// gca_r_in
//always @(posedge I_sclk or negedge I_rst_n) begin
//    if (~I_rst_n)
//        gca_r_in <= 1'b0;
//    else if (la_valid && gca_color[0])
//        gca_r_in <= la_data_out;
//end
//
//// gca_g_in
//always @(posedge I_sclk or negedge I_rst_n) begin
//    if (~I_rst_n)
//        gca_g_in <= 1'b0;
//    else if (la_valid && gca_color[1])
//        gca_g_in <= la_data_out;
//end
//
//// gca_color
//always @(posedge I_sclk or negedge I_rst_n) begin
//    if (~I_rst_n)
//        gca_color <= 3'b001;
//    else if (start_sr[L2])
//        gca_color <= 3'b001;
//    else
//        gca_color <= {gca_color[1:0], gca_color[2]};
//end
////}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++gap adjust+++++++++++++++++++++
assign ga_data_en = la_valid;
assign ga_data_in = la_data_out;
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++pixel color adjust+++++++++++++
//assign pca_data_en = gca_valid;
//assign pca_r_in    = gca_r_out;
//assign pca_g_in    = gca_g_out;
//assign pca_b_in    = gca_b_out;
assign pca_data_en = ga_valid;
assign pca_r_in    = ga_r_out;
assign pca_g_in    = ga_g_out;
assign pca_b_in    = ga_b_out;
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++output result++++++++++++++++++
assign pixel_data = {pca_b_out, pca_g_out, pca_r_out};
assign raw_pixel = {pixel_data, pixel_buf};

// pixel_buf
always @(posedge I_sclk) begin
    if (pca_valid)
        pixel_buf <= pixel_data;
end

// odd_flag
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        odd_flag <= 1'b0;
    else if (pca_valid)
        odd_flag <= ~odd_flag;
end

// result_valid
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        result_valid <= 1'b0;
    else if (pca_valid && odd_flag)
        result_valid <= 1'b1;
    else if (cnt2 == 2'd3)
        result_valid <= 1'b0;
end

// cnt2
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        cnt2 <= 1'b0;
    else if (pca_valid && odd_flag)
        cnt2 <= 1'b0;
    else if (result_valid)
        cnt2 <= cnt2 + 1'b1;
end

// NOTE: 在每2个像素的最后一个像素处理完成后，立刻进行交织
// result
integer i, j;
always @(posedge I_sclk) begin
    if (pca_valid && odd_flag) begin
        for (i = 0; i < 16; i = i + 1) begin
            for (j = 0; j < 2; j = j + 1) begin
                result[i*6+j*3+0] <= raw_pixel[i+j*48+0];
                result[i*6+j*3+1] <= raw_pixel[i+j*48+16];
                result[i*6+j*3+2] <= raw_pixel[i+j*48+32];
            end
        end
    end
    else if (result_valid)
        result <= result >> 24;
end

assign O_result_valid = result_valid;
assign O_result_data  = {
    1'b0, result[23:21],
    1'b0, result[20:18],
    1'b0, result[17:15],
    1'b0, result[14:12],
    1'b0, result[11:9],
    1'b0, result[8:6],
    1'b0, result[5:3],
    1'b0, result[2:0]
};
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
